
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;


public class PCBList {
	
	/**
	 * Each process will have a fixed number of instructions
	 */
	protected final static int NUM_INSTRUCTIONS = 10000;
	
	/**
	 * Each process will have a fixed number of instructions
	 */
	protected final static int NUM_REQUESTS = 2500;
	
	/**
	 * A list of Pcbs
	 */
	private List<Pcb> my_pcbs;
	
	/**
	 * A map that links a pcb id to Pcb. Is the producer map
	 */
	private Map<Integer, Pcb> prod_mutex_map;
	
	/**
	 * A map that links a pcb id to Pcb. Is the consumer map.
	 */
	private Map<Integer, Pcb> cons_mutex_map;
	
	/**
	 * A mutex field to help with the methods
	 */
	private int mutex;
	
	
	public PCBList(final int io_processes, final int compute_processes, 
			       final int prod_cons_processes) {
		
		my_pcbs = new ArrayList<Pcb>();
		
		int id = 0;
		mutex = 0;
		
		prod_mutex_map = new HashMap<Integer, Pcb>();
		cons_mutex_map = new HashMap<Integer, Pcb>();
		
		id = makeKeyProcesses(1, id);
		id = makeIoProcesses(io_processes, id);
		id = makeCompProcesses(compute_processes, id);
		id = makeProConProcesses(prod_cons_processes, id);
	}

	/**
	 * Makes ui Processes
	 * @param ui_processes The number of processes to make
	 * @param id The next free id
	 * @return The id for the next method to use.
	 */
	private int makeKeyProcesses(final int ui_processes, int id) {
		
		for(int i = 0; i <ui_processes ; i++) {
			genRandomAddress();
			final Pcb pcb = new Pcb(id, 0, 1, new Process(2, NUM_INSTRUCTIONS, 
					         NUM_REQUESTS, (LinkedList<Integer>) genRandomAddress()), mutex);
			
			my_pcbs.add(pcb);
			
			id++;
			mutex++;
		}
		
		return id;
		
	}
	

	/**
	 * Makes io Processes
	 * @param io_processes The number of processes to make
	 * @param id The next free id
	 * @return The id for the next method to use.
	 */
	private int makeIoProcesses(final int io_processes, int id) {
	
		for(int i = 0; i <io_processes ; i++) {
			genRandomAddress();
			final Pcb pcb = new Pcb(id, 0, 1, new Process(1, NUM_INSTRUCTIONS, 
					         NUM_REQUESTS, (LinkedList<Integer>) genRandomAddress()), mutex);
			
			my_pcbs.add(pcb);
			
			id++;
			mutex++;
		}
		
		return id;
		
	}
	
	
	
	/**
	 * Makes computational Processes
	 * @param compute_processes The number of processes to make
	 * @param id The next free id
	 * @return The id for the next method to use.
	 */
	private int makeCompProcesses(final int compute_processes, int id) {
		for(int i = 0; i <compute_processes; i++) {
			genRandomAddress();
			final Pcb pcb = new Pcb(id, 0, 1, new Process(0, NUM_INSTRUCTIONS, 
					         NUM_REQUESTS, (LinkedList<Integer>) genRandomAddress()), mutex);
			
			my_pcbs.add(pcb);
			
			id++;
			mutex++;
		}
		
		return id;
	}
	
	
	/**
	 * Makes Producer-consumer Processes
	 * @param prod_cons_processes The number of processes to make
	 * @param id The next free id
	 * @return The id for the next method to use.
	 */
	private int makeProConProcesses(final int prod_cons_processes, int id) {
		for(int i = 0; i < prod_cons_processes; i++) {
			genRandomAddress();
			final Pcb producer = new Pcb(id, 0, 1, new Process(0, NUM_INSTRUCTIONS, 
					         NUM_REQUESTS, (LinkedList<Integer>) genRandomAddress()), mutex);
			id++;
			
			final Pcb consumer = new Pcb(id, 0, 1, new Process(0, NUM_INSTRUCTIONS, 
			         NUM_REQUESTS, (LinkedList<Integer>) genRandomAddress()), mutex);
			
			prod_mutex_map.put(producer.getMutex(), producer);
			cons_mutex_map.put(consumer.getMutex(), consumer);
			
			my_pcbs.add(producer);
			my_pcbs.add(consumer);
			
			id++;
			mutex++;
		}
		
		return id;
		
	}
	
	
	/**
	 * private method to generate a random instruction address
	 * @return A list of addresses where the service requests will be made.
	 */
	private List<Integer> genRandomAddress() {
		final Random r = new Random();
		final List<Integer> add_list = new LinkedList<Integer>();
		
		for(int i = 0; i < NUM_REQUESTS; i++) {
			add_list.add(r.nextInt(NUM_INSTRUCTIONS));
		}
		
		return add_list;
	}
	
	
	/**
	 * Returns the associated consumer pcb from the mutex.
	 * 
	 * @param mutex An integer that its the key in the map.
	 * @return The PCB that the mutex corresponds to
	 */
	protected Pcb getConFromMutex(final int mutex) {
		return cons_mutex_map.get(mutex);
		
	}
	
	
	/**
	 * Returns the associated producer pcb from the mutex.
	 * 
	 * @param mutex An integer that its the key in the map.
	 * @return The PCB that the mutex corresponds to
	 */
	protected Pcb getProFromMutex(final int mutex) {
		return prod_mutex_map.get(mutex);
		
	}

	public String toString() {
		final StringBuilder str = new StringBuilder();
		for(final Pcb pcb : my_pcbs) {
			str.append("Process " + pcb.getID() + " (" +pcb.getProcess().toString() + 
					    ")'s state: " + pcb.stateString() + "\n");
			
		}
		return str.toString();
	}

	protected int size()
	{
		return my_pcbs.size();
	}
	
	public static void main() {
		
	}

	public Pcb get(int i) 
	{
		return my_pcbs.get(i);
	}

	public void add(Pcb pcb) 
	{
		my_pcbs.add(pcb);
		
	}

	public Pcb remove(int i) 
	{
		return my_pcbs.remove(i);
	}
	
	public void sort()
	{
		
		Collections.sort(my_pcbs, new Comparator<Pcb>() {
	        @Override
	        public int compare(Pcb  process1, Pcb  process2)
	        {
	        	Integer a =  new Integer(process1.my_priority);
	        	Integer b =  new Integer(process2.my_priority);
	        	
	        	return a.compareTo(b);
	        	
	        }
	    });
		
		
	}
	
	public final List<Pcb> getList()
	{
		return my_pcbs;
	}

}

